import axios from 'axios'
import type { AxiosInstance } from 'axios'
import type { IRequestConfig } from './index.model'

// 拦截器: 蒙版Loading/token/修改配置

/**
 * 两个难点:
 *  1.拦截器进行精细控制
 *    > 全局拦截器
 *    > 实例拦截器
 *    > 单次请求拦截器
 *
 *  2.响应结果的类型处理(泛型)
 */

class RequestModel {
    instance: AxiosInstance
    isLoading: boolean = true

    // request实例 => axios的实例
    constructor(config: IRequestConfig) {
        this.instance = axios.create(config)
        this.isLoading = true

        // 每个instance实例都添加拦截器
        // 请求拦截器
        this.instance.interceptors.request.use((config) => {
            // loading/token
            this.isLoading = true
            return config
        }, (err) => {
            this.isLoading = false
            return err
        })
        // 响应拦截器
        this.instance.interceptors.response.use((res) => {
            this.isLoading = false
            return res.data
        }, (err) => {
            this.isLoading = false
            return err
        })

        // 针对特定的hyRequest实例添加拦截器
        this.instance.interceptors.request.use(
            config.interceptors?.requestSuccessFn,
            config.interceptors?.requestFailureFn
        )
        this.instance.interceptors.response.use(
            config.interceptors?.responseSuccessFn,
            config.interceptors?.responseFailureFn
        )
    }

    // 封装网络请求的方法
    // T => IHomeData
    request<T = any>(config: IRequestConfig<T>) {
        // 单次请求的成功拦截处理
        if (config.interceptors?.requestSuccessFn) {
            config = config.interceptors.requestSuccessFn(config)
        }

        // 返回Promise
        return new Promise<T>((resolve, reject) => {
            this.instance
                .request<any, T>(config)
                .then((res) => {
                    // 单次响应的成功拦截处理
                    if (config.interceptors?.responseSuccessFn) {
                        res = config.interceptors.responseSuccessFn(res)
                    }
                    resolve(res)
                })
                .catch((err) => {
                    reject(err)
                })
        })
    }

    get<T = any>(config: IRequestConfig<T>) {
        return this.request({ ...config, method: 'GET' })
    }
    post<T = any>(config: IRequestConfig<T>) {
        return this.request({ ...config, method: 'POST' })
    }
    delete<T = any>(config: IRequestConfig<T>) {
        return this.request({ ...config, method: 'DELETE' })
    }
    patch<T = any>(config: IRequestConfig<T>) {
        return this.request({ ...config, method: 'PATCH' })
    }
}

export default RequestModel

/**
 * 如果发起 get 请求，上述代码的执行流程如下：
 * 1. 首先创建一个 RequestModel 实例，传入相应的配置函数
 * 2. 在 RequestModel 构造函数中 创建一个 axios 实例， 并添加请求和响应拦截器
 * 3. 调用 get 方法， 该方法调用 request 方法， 并传入相应的配置参数和请求方法（GET）
 * 4. 在 request 方法中，首先对请求进行拦截处理，例如loading 等
 * 5. 然后，使用 Axios 实例中的 request 方法发送请求，并通过 Promise 包装返回结果
 * 6. 当请求成功返回时，会对响应进行拦截处理，如处理响应数据的类型等
 * 7. 最后返回经过处理的响应结果
 * 
 * 总结：创建 RequestModel 实例 -> 调用 get 方法 -> 调用 request 方法 -> 
 * 拦截处理请求 -> 发送请求 -> 拦截处理响应 -> 返回响应结果
 */
